12. Edge Case: Capturing Variables

Edge Case: Capturing Variables

ND079 JPND C2 L01 A09 Edge Case Capturing Variables V2

Captured Variables

Lambdas can capture variables from the surrounding code. If a lambda uses any variables from the surrounding code, those variables are captured. Variables can only be captured if they are effectively final.

An effectively final variable is a variable whose value does not change after it is initialized.

Example

Map<Year, Integer> getClassSizes(List<Student> students) {
  final Map<Year, Integer> classSizes = new HashMap<>();
  students.stream().forEach(s ->
      classSizes.compute(
          s.getGraduationYear(),
          (k, v) -> (v == null) ? 1 : 1 + v));
  return classSizes;
}

A good test to figure out if a variable is effectively final is to add the final keyword to it. If the code still compiles, that variable is effectively final!

In the example, the classSizes variable is effectively final because the value of the variable itself does not change after it's initialized. Remember that in Java, objects are passed by reference. Even though the HashMap changes, the variable's value is the HashMap's location in memory, and that location never changes.

What is true of effectively final variables?

SOLUTION:
  • The value of the variable is never changed after it's first initialized.
  • If you add the `final` keyword to the variable, the code will still compile.

void printResult(List<String> input, StringWriter output) {
 Map<Integer, String> bySize = new HashMap<>();
 input.stream().forEach(
    (String s) -> bySize.put(s.length(), s));
 output.write(generateSummary(bySize));
}

Which variable(s) are captured in the lambda?

SOLUTION:
  • `bySize`

List<Runnable> runnables = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
  runnables.add(() -> System.out.println(i));
}

In the code snippet above, a lambda captures the variable i. Is i effectively final?

SOLUTION: No